#pragma once

#include <physics/Registrator.h>
#include <physics/Fixture.h>
#include <physics/Types.h>

struct b2BodyDef;
class b2World;

namespace liba { namespace lib3d { namespace node { class Node; }}}

namespace physics
{

class World;
struct Contact;

class Node: public Fixture
{
	PHYSICS_NODE_REGISTER(Node)

	friend class NodeLoader;

private:
	World * world;
	void * userData;

	std::auto_ptr <b2BodyDef> bodyDef;

	liba::lib3d::node::Node *sceneNode;
	bool attachedToNode;
	
	std::vector<Contact> contacts;
	
private:
	void UpdateContacts();

protected:
// properties
	Atom name;
	Atom sceneNodeName;

	liba::lib3d::node::Node *rootNode;

protected:
	void CreateDefaultBody();

	virtual bool OnSetRootNode(liba::lib3d::node::Node *node) { return false; }

public:
	Node(World *world);
	virtual ~Node();

	virtual std::auto_ptr <xml::ObjectANSI> CreateLoader();

	virtual void SetRootNode(liba::lib3d::node::Node *node);

	virtual void SetSceneNode(liba::lib3d::node::Node *node, bool attachedToNode = false);
	virtual liba::lib3d::node::Node * GetSceneNode() const;

	virtual void UpdateKinematic();
	virtual void Update();
	virtual void OnUpdate(float dt) {}

	World* GetWorld() const { return world; }
	
	void SetPosition(const Vector2 & position);
	Vector2 GetPosition() const;

	void SetRotation(float angle);
	float GetRotation() const;
	
	void SetFixedRotation(bool flag);
	
	int GetContactsCount() const;
	Contact GetContact(unsigned int index) const;
	
	void SetName(const Atom & newName);
	const Atom& GetName() const;
	
	void AddForce(const Vector2 & point, const Vector2 & force);
	void AddCentralForce(const Vector2 & force);
	void AddTorque(float torque);
	void AddLinearImpulse(const Vector2 & point, const Vector2 & impulse);
	void AddAngularImpulse(float torque);
	void SetLinearVelocity(const Vector2 & velocity);
	void SetAngularVelocity(float velocity);
	
	Vector2 GetLinearVelocity() const;
	float GetAngularVelocity() const;
	
	void SetMass(float mass);
	float GetMass() const;

	b2Body* GetBody() const { return body; }
	b2World* GetB2World() const;

	bool TestPoint(const Vector2 &pos) const;

	void Release();
	
	void DisableGravity();
	void EnableGravity();
	
	bool IsStatic() const;
	void MakeStatic();
	void MakeDynamic();
	
	virtual void SetCenter(const Vector2 & center) {}
	virtual Vector2 GetCenter() const { return Vector2::zero(); }
	virtual Vector2 GetSize() const { return Vector2::zero(); }
	
	void SetUserData(void * userData);
	void* GetUserData();
	
	
	float GetWorldPixelsInMeter() const;
};

}	// namespace physics